package com.tjbank.cssys.service.config.shiro;

import com.tjbank.cssys.service.config.shiro.stateless.token.TokenGenerator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Copyright © 天阳宏业科技股份有限公司 - All Rights Reserved
 *
 * @description:
 * @author: <a href="mailto:guzheng01@tansun.com.cn>Joey Gu</a>
 * @date: 2020-06-09 09:43
 **/
@Component
public class ShiroTokenCacheManager {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 用户登录回话互斥,即同一用户只能一人在线
     */
    @Value("${shiro.sessionMutex:true}")
    private Boolean sessionMutex;

    /**
     * 回话超时时间, 单位秒
     */
    @Value("${shiro.sessionTimeout:1800}")
    private Integer sessionTimeout;

    public static final String TOKEN_SEED = "token_seed";

    /**
     * Redis的命名空间
     */
    @Value("${spring.application.name}")
    private String redisNamespace;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private static String localSeedValue = null;

    /**
     * 获得当前系统的 token seed
     */
    public String findSeed() throws Exception {
        if (localSeedValue != null) {
            return localSeedValue;
        } else {
            String seed = getSeedValue(TOKEN_SEED);
            if (StringUtils.isBlank(seed)) {
                seed = TokenGenerator.getSeed();
                localSeedValue = seed;
                redisTemplate.opsForValue().set(redisNamespace + ":" + TOKEN_SEED, seed);
            }
            return seed;
        }
    }

    public String getSeedValue(String key) {
        return (String) redisTemplate.opsForValue().get(redisNamespace + ":" + key);
    }


    /**
     * 删除session缓存
     *
     * @param key mock的sessionid
     */
    public void removeSessionCache(String key) {
        redisTemplate.delete(redisNamespace + ":" + key);
    }


    private String getCurrentTimeSeconds() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

    public void registOnlineSession(final String userName, final String token) {

        // 是否互斥，如果是，则踢掉所有当前用户的session，重新创建，此变量将来从配置文件读取
        if (sessionMutex) {
            deleteUserSession(userName);
        } else {
            // 清理此用户过期的session，过期的常为异常或者直接关闭浏览器，没有走正常注销的key
            clearOnlineSession(userName);
        }

        redisTemplate.opsForHash().put(userName, token, getCurrentTimeSeconds());
        int timeout = sessionTimeout;
        if (timeout > 0) {
            redisTemplate.expire(userName, timeout, TimeUnit.SECONDS);
        }
    }

    private void clearOnlineSession(final String key) {
        redisTemplate.opsForHash().keys(key).forEach((obj) -> {
            String hashKey = (String) obj;
            int timeout = sessionTimeout;
            if (timeout > 0) {
                int oldTimeSecondsValue = Integer.valueOf((String) Objects.requireNonNull(redisTemplate.opsForHash().get(key, hashKey)));
                int curTimeSecondsValue = (int) (System.currentTimeMillis() / 1000);
                //如果 key-hashKey 对应的时间+过期时间 小于 当前时间，则剔除
                if (curTimeSecondsValue - (oldTimeSecondsValue + timeout) > 0) {
                    redisTemplate.opsForHash().delete(key, hashKey);
                }
            }
        });
    }

    public boolean validateOnlineSession(final String key, final String hashKey) {
        int timeout = sessionTimeout;
        if (timeout > 0) {
            String oldTimeSecondsValue = (String) redisTemplate.opsForHash().get(key, hashKey);
            if (StringUtils.isEmpty(oldTimeSecondsValue)) {
                return false;
            } else {
                int curTimeSecondsValue = (int) (System.currentTimeMillis() / 1000);
                if (Integer.valueOf(oldTimeSecondsValue) + timeout >= curTimeSecondsValue) {
                    //刷新 key
                    redisTemplate.opsForHash().put(key, hashKey, getCurrentTimeSeconds());
                    redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
                    return true;
                } else {
                    redisTemplate.opsForHash().delete(key, hashKey);
                    return false;
                }
            }
        } else {
            return redisTemplate.opsForHash().get(key, hashKey) != null;
        }
    }

    /**
     * 注销用户时候需要调用
     */
    public void delOnlineSession(final String key, final String hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 禁用或者删除用户时候调用
     *
     * @param key redis key
     */
    public void deleteUserSession(final String key) {
        redisTemplate.delete(key);
    }
}
